package com.gmrz.uaf.remote.protocol.v1.processor;

import com.gmrz.service.challengestore.ChallengeStoreException;
import com.gmrz.uaf.common.Constants;
import com.gmrz.uaf.common.UAFCommonRuntimeException;
import com.gmrz.uaf.common.UAFRuntimeException;
import com.gmrz.uaf.db.DAOException;
import com.gmrz.uaf.db.UAFDAOFactory;
import com.gmrz.uaf.db.UAFDBConnectionMgr;
import com.gmrz.uaf.db.dao.AuthenticatorDAO;
import com.gmrz.uaf.protocol.v1.json.UAFSchemaBuilder;
import com.gmrz.uaf.protocol.v1.processor.UAFBaseProcessor;
import com.gmrz.uaf.protocol.v1.processor.exception.AuthenticationFailedException;
import com.gmrz.uaf.protocol.v1.processor.exception.PolicyNotFoundException;
import com.gmrz.uaf.protocol.v1.processor.exception.PossibleReplayAttackException;
import com.gmrz.uaf.protocol.v1.processor.exception.UAFErrorCode;
import com.gmrz.uaf.protocol.v1.schema.*;
import com.gmrz.uaf.protocol.v1.validaton.ValidationException;
import com.gmrz.uaf.protocol.v1.validaton.Validator;
import com.gmrz.uaf.remote.plugin.AuthServicePluginManager;
import com.gmrz.uaf.remote.protocol.v1.validation.auth.UAPIDAuthResponseValidator;
import com.gmrz.util.UUIDGenerator;
import com.gmrz.util.db.DBUtil;
import com.google.inject.Inject;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;
import java.util.Set;


public class UAPOOBAuthFinishProcessor extends UAFBaseProcessor{
    private static final Logger LOG = LogManager.getLogger(UAPOOBAuthFinishProcessor.class);

    AuthServicePluginManager serviceManager;

    @Inject
    public void setServiceManager(AuthServicePluginManager serviceManager) {
        this.serviceManager = serviceManager;
    }

    /**
     *
     * @param response
     * @param appID
     * @param authType
     * @param hashedUserName
     * @param transType
     * @param authToken
     * @param authData
     * @throws PossibleReplayAttackException
     * @throws ChallengeStoreException
     * @throws PolicyNotFoundException
     * @throws DAOException
     * @throws ValidationException
     */
    public void process(AuthenticationResponse response, String appID, String authType, String hashedUserName,String transType,String authToken,String authData) throws PossibleReplayAttackException, ChallengeStoreException, PolicyNotFoundException, DAOException, ValidationException {

        LOG.info("UAPOOBAuthFinishProcessor Validating authentication response");

        Validator<AuthenticationResponse> authRespValidator = new UAPIDAuthResponseValidator(this.serverConfig,
                Constants.Operation.REG,appID,transType);
        authRespValidator.validate(response);

        ServerData serverData = response.getOperationHeader().getServerData();
        checkForReplayAttack(serverData);

        List<Extension> exts = response.getOperationHeader().getExtensions();
        processExtensions(exts);

        TLSData tlsData = response.getFinalChallengeParams().getTLSData();
        processTLSData(tlsData);

        String plainUserName = serverData.getUserName();

        String policyName = serverData.getPolicyName();
        LOG.info("Auth Response userAliasName[{}], policyname[{}]", plainUserName, policyName);

        AuthenticatorRegistration authentication = response.getAuthentication();
        if(authentication == null){
            String msg = "No AuthenticatorRegistration found in the registration response for user[" + plainUserName + "], policy["
                    + policyName + "].";

            logAndThrowAFE(msg);
        }

        String aaid = authentication.getAaid();
        Policy p = this.policyManager.getNamedPolicy(authType,appID,transType);
        LOG.info("Policy:" + UAFSchemaBuilder.getGson().toJson(p));
        if(p == null || !isAcceptedAaid(p, aaid)){
            LOG.error("Requested policy {} is not available on server", "POLICY_"+appID+"_"+authType);
            throw new PolicyNotFoundException(UAFErrorCode.BIZ_POLICY_NOT_FOUND);
        }
        // 更新缓存中口令对应的二维码的状态
        process(authToken,authData,Constants.OOBStatus.APP_AUTH.getStatus());


    }
    protected void logAndThrowAFE(String msg) {
        LOG.error(msg);
        throw new AuthenticationFailedException(msg);
    }

    public void process(String authToken,String authData,int status) throws ChallengeStoreException {
        OOBInfo oobInfo = this.getCacheOOBInfo(authToken);
        oobInfo.setAuthData(authData);
        oobInfo.setStatus(status);
        this.cacheOOBInfo(oobInfo,authToken);
    }

    public void updateOOBCode(String authToken,String authAppID, String deviceID, int status, String authID) throws SQLException {
        Connection conn = UAFDBConnectionMgr.getConnection();
        try {
            AuthenticatorDAO adao = UAFDAOFactory.createAuthenticatorDAO(conn);
            OOBCodeBean oobCodeBean = new OOBCodeBean();
            oobCodeBean.setAuthToken(authToken);
            oobCodeBean.setAuthAppID(authAppID);
            oobCodeBean.setDeviceID(deviceID);
            oobCodeBean.setCodeStatus(status);
            oobCodeBean.setAuthID(authID);
            adao.updateOOBCode(oobCodeBean);

        }catch (DAOException e1){
            LOG.error("update oob code error",e1);
        }finally {
            DBUtil.close(conn);
        }
    }

    public void updateCertStatus(String appID,String authID,String activateType,Authenticator authenticator) throws SQLException, DAOException, ChallengeStoreException {
        Connection conn = UAFDBConnectionMgr.getConnection(false);
        try {
            AuthenticatorDAO adao = UAFDAOFactory.createAuthenticatorDAO(conn);


            String value = this.getServerConfig().getUasConfig(Constants.UAF_ACTIVITY_ONLY_AUTHENTICATOR);
            if(com.gmrz.util.Strings.isNotBlank(value)) {
                Integer v = Integer.parseInt(value);
                if (v > 0) {
                    Set<Authenticator> authenticators = adao.findUserNoDeviceDer(authenticator.getUserName(), authenticator.getAppID(), authenticator.getAuthType(), authenticator.getTransType(), Constants.AuthenticatorStatus.ACTIVE.getStatus());
                    if (null != authenticators) {
                        for (Authenticator a : authenticators) {
                            if (a.getCertificateExtendBean().getCertStatus() == Constants.CERT_STATUS_3 && !authID.equals(a.getID())) {
                                adao.updateCertStatus(a.getID(), Constants.CERT_STATUS_3, Constants.CERT_STATUS_2);
                            }
                        }
                    }
                }
            }

            int n = adao.updateCertStatus(authID,Constants.CERT_STATUS_2,Constants.CERT_STATUS_3);
            if(n <= 0){
                throw new UAFCommonRuntimeException(UAFErrorCode.PROTOCOL_ACTIVITY_CERT_STATUS_FAILED);
            }
            adao.updateCertExtend(appID,authID,activateType);
            conn.commit();
        }catch (DAOException e1){
            conn.rollback();
            LOG.error("updateCertStatus error",e1);
        }finally {
            DBUtil.close(conn);
        }


    }

    public OOBCodeBean getOOBCode(String authToken) throws SQLException, DAOException{
        Connection conn = UAFDBConnectionMgr.getConnection();
        try {
            AuthenticatorDAO adao = UAFDAOFactory.createAuthenticatorDAO(conn);
            return adao.findOOBCode(authToken);
        }catch (DAOException e1){
            LOG.error("get OOBCode error",e1);
        }finally {
            DBUtil.close(conn);
        }
        return null;
    }
}
